//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <ddk.h>
#include <bulverde.h>
#include <drvpub.h>
#include <memdefs.h>
#include "dmac.h"
#include <INTCBits.h>
#include "audiocard.h"

#define AO_NEXT_BUFFER(idx) ((idx == pCxt->aoBufCnt - 1) ? 0 : (idx + 1))
#define AI_NEXT_BUFFER(idx) ((idx == pCxt->aiBufCnt - 1) ? 0 : (idx + 1))

#if (_TEST_TYPE == 41)      /* Test for Runtime of ISR */
#define      SAMPLETIMES  50
static UInt32 ulDmaChOutIsrTime[SAMPLETIMES];
static UInt32 ulDmaChRcvIsrTime[SAMPLETIMES];
static Int32 nDmaChOutIsrCounter = 0;
static Int32 nDmaChRcvIsrCounter = 0;
#endif //_TEST_TYPE == 41

// There are some bugs for recording when using automatically reset event.
static DzEvent s_syncAudioIn(TRUE, UNSIGNALED);
static DzEvent s_syncAudioOut(TRUE, UNSIGNALED);

#ifdef DEBUG_AUDIO
void DumpDmacRegs(int channel)
{
    kprintf("Dump DMA registers for channel %d\n",channel);

    kprintf("DINT       = %x \n", DINT);
    kprintf("DCSR       = %x\n", DCSR(channel));
    kprintf("DDADR      = %x\n", DDADR(channel));
    kprintf("DSADR      = %x\n", DSADR(channel));
    kprintf("DTADR      = %x\n", DTADR(channel));
    kprintf("DCMD       = %x\n", DCMD(channel));
}

void DumpAc97LinkRegs()
{
    kprintf("Dump AC97Link registers\n");

    kprintf("GCR        =%x\n", GCR);
    kprintf("GSR        =%x\n", GSR);
    kprintf("POCR       =%x\n", POCR);
    kprintf("POSR       =%x\n", POSR);
    kprintf("PCMICR     =%x\n", PCMICR);
    kprintf("PCMISR     =%x\n", PCMISR);
    kprintf("CAR        =%x\n", CAR);
}

void DumpAC97Regs(void)
{
    Byte i;
    UInt16 Data;

    kprintf("*********************************************\n");
    kprintf("DumpAC97Regs below:\n");

    for (i = 0; i < 0x7e; i += 2) {
        Ac97_Read(i, &Data);
        kprintf("reg[0x%x]=0x%04x  ", i, Data);
        if ((i % 8) == 0) kprintf("\n");
    }
    kprintf("\n*********************************************\n");
}
#endif

// In These copy functions, parameter cnt means count of bytes
void memcpy_m16_s16(Byte *pDes, Byte *pSrc, UInt32 cnt)
{
    UInt32 i = 0;
    UInt16 *pSrc16 = (UInt16 *)pSrc;
    UInt16 *pDes16 = (UInt16 *)pDes;

    while (i < cnt) {
        *pDes16 = *pSrc16++;
        *(pDes16 + 1) = *pDes16;
        pDes16 += 2;
        i += 2;
    }
}

void memcpy_m8_s16(Byte *pDes, Byte *pSrc, UInt32 cnt)
{
    UInt32 i = 0;
    UInt16 *pDes16 = (UInt16 *)pDes;

    while (i < cnt) {
        *pDes16 = BITS_8_TO_16(*pSrc++);
        *(pDes16 + 1) = *pDes16;
        pDes16 += 2;
        i++;
    }
}

void memcpy_s8_s16(Byte *pDes, Byte *pSrc, UInt32 cnt)
{
    UInt32 i = 0;
    UInt16 *pDes16 = (UInt16 *)pDes;

    while (i < cnt) {
        *pDes16++ = BITS_8_TO_16(*pSrc++);
        *pDes16++ = BITS_8_TO_16(*pSrc++);
        i += 2;
    }
}

void memcpy_s16_s16(Byte *pDes, Byte *pSrc, UInt32 cnt)
{
    memcpy(pDes, pSrc, cnt);
}

void memcpy_s16_m8(Byte *pDes, Byte *pSrc, UInt32 cnt)
{
    UInt32 i = 0;
    UInt16 *pSrc16 = (UInt16 *)pSrc;
    Byte SrcLeft;
//    Byte SrcRight;

    // To get clear wave data, use left channel
    while (i < cnt) {
        SrcLeft = BITS_16_TO_8(*pSrc16++);
//        SrcRight = BITS_16_TO_8(*pSrc16++);
        *pSrc16++;
//        *pDes = (SrcLeft >> 1) + (SrcRight >> 1);
        *pDes = SrcLeft;
        pDes++;
        i++;
    }
}

void memcpy_s16_m16(Byte *pDes, Byte *pSrc, UInt32 cnt)
{
    UInt32 i = 0;
    UInt16 *pSrc16 = (UInt16 *)pSrc;
    UInt16 *pDes16 = (UInt16 *)pDes;
    UInt16 SrcLeft;
//    UInt16 SrcRight;

    // To get clear wave data, use left channel
    while (i < cnt) {
        SrcLeft = *pSrc16++;
//        SrcRight = *pSrc16++;
        pSrc16++;
//        *pDes16 = (SrcLeft >> 1) + (SrcRight >> 1);
        *pDes16 = SrcLeft;
        pDes16++;
        i += 2;
    }
}

void memcpy_s16_s8(Byte *pDes, Byte *pSrc, UInt32 cnt)
{
    UInt32 i = 0;
    UInt16 *pSrc16 = (UInt16 *)pSrc;

    while (i < cnt) {
        *pDes++ = BITS_16_TO_8(*pSrc16++);
        *pDes++ = BITS_16_TO_8(*pSrc16++);
        i += 2;
    }
}

// Fill the DMA descriptors with appropriate data
// dir == STATE_AUDIOOUT: Playing
// dir == STATE_AUDIOIN: Recording
void FillDescriptors(AudioContext *pCxt, UInt32 dir, UInt32 idx, UInt32 len)
{
    DMADescriptorChannelType *pDesc;
    union DmaCmdReg CmdBuff;

    CmdBuff.DcmdDword = 0; // Set all reserved bits to be 0

    assert(len <= AUDIO_BUFSIZE);
    len &= 0x1FE0; // 32 bytes align, max value for DMA hardware is 8K - 32 bytes

    // set values with bit fields
    CmdBuff.DcmdReg.len = len;  //length of the memory buffer
    CmdBuff.DcmdReg.width = 0x3;    // binary 11 (see quick Quick Reference sheet to DMA programming in the cotulla EAS)
    CmdBuff.DcmdReg.size = 0x3;     // binary 11
    CmdBuff.DcmdReg.endian = 0;     // little endian
    CmdBuff.DcmdReg.flybyt = 0;     // Flowthrough
    CmdBuff.DcmdReg.flybys = 0;     // Flowthrough
    CmdBuff.DcmdReg.endirqen = 1;   // 1 means Interrupt when decrement length = 0;
    CmdBuff.DcmdReg.startirqen = 0; // 1 means Interrupt when the desc is loaded
    if (dir == STATE_AUDIOOUT) {
        CmdBuff.DcmdReg.flowtrg = 1;    // 1 means the target is an external peripheral
        CmdBuff.DcmdReg.flowsrc = 0;    // 1 means the source is an external peripheral (and needs flow control)
        CmdBuff.DcmdReg.inctrgadd = 0;  // 1 means increment the target address (since it's memory)
        CmdBuff.DcmdReg.incsrcadd = 1;  // 1 means increment the source address (since it's a peripheral)
        // address of the next descriptor
        pDesc = (DMADescriptorChannelType *)
            (pCxt->aoDmaDescVirtAddr + idx * DMA_DESC_SIZE);
        pDesc->ddadr = pCxt->aoDmaDescPhysAddr + AO_NEXT_BUFFER(idx) * DMA_DESC_SIZE;
        pDesc->dtadr = DMAC_AC97_AUDIO_XMIT_FIFO;  // destination address
        pDesc->dsadr = pCxt->aoBufferPhysAddr + idx * AUDIO_BUFSIZE; // source address
    }
    else {
        CmdBuff.DcmdReg.flowtrg = 0;    // 1 means the target is an external peripheral
        CmdBuff.DcmdReg.flowsrc = 1;    // 1 means the source is an external peripheral (and needs flow control)
        CmdBuff.DcmdReg.inctrgadd = 1;  // 1 means increment the target address (since it's memory)
        CmdBuff.DcmdReg.incsrcadd = 0;  // 1 means increment the source address (since it's a peripheral)
        // address of the next descriptor
        pDesc = (DMADescriptorChannelType *)
            (pCxt->aiDmaDescVirtAddr + idx * DMA_DESC_SIZE);
        pDesc->ddadr = pCxt->aiDmaDescPhysAddr + AI_NEXT_BUFFER(idx) * DMA_DESC_SIZE;
        pDesc->dtadr = pCxt->aiBufferPhysAddr + idx * AUDIO_BUFSIZE;  // destination address
        pDesc->dsadr = DMAC_AC97_AUDIO_RCV_FIFO; // source address
    }

    pDesc-> dcmd = CmdBuff.DcmdDword;   // size and cmd values
}

// Populate DMAC Descriptors and fill DMA registers with descriptor pointers,
// Start DMA transmit
Boolean InitDMAC(AudioContext *pCxt, int Channel)
{
    // 1. Set the physical address of the frame descriptors
    // 2. setup the request channel map register (with channel and valid)
    // 3. Load the address of the first descriptor into the channel group. The rest of
    //    the registers are loaded when the RUN bit is set in the DCSR.

    DBGOUT(DBG_LOG, ("InitDmac, channel %d\n", Channel));
    assert(Channel == pCxt->aoDmaCh || Channel == pCxt->aiDmaCh);

    if (Channel == pCxt->aoDmaCh) {
        DDADR(Channel)= pCxt->aoDmaDescPhysAddr;
        DRCMR12 = DMA_MAP_VALID_MASK | pCxt->aoDmaCh;
        POSR =  0x10;
    }
    else if (Channel == pCxt->aiDmaCh) {
        DDADR(Channel)= pCxt->aiDmaDescPhysAddr;
        DRCMR11 =  DMA_MAP_VALID_MASK | pCxt->aiDmaCh;
    }

    DCSR(Channel) |=  DCSR_RUN;      // set the RUN bit

    return TRUE;
}

// To stop DMA transmit
Boolean  StopDmac(int Channel)
{
    //TODO: Do I need to set the dma length to 0
    DCSR(Channel) &=  ~DCSR_RUN;  //clear the run but

    return TRUE;
}

// Convert size of bytes from user input (8/16 bits, 1/2 channels)
// to hardware transfer(16 bits, 2 channels)
INLINE GetFillSize(UInt32 nCopySize, UInt32 nBits, UInt32 nChannels)
{
    int nFillSize;

    if (nBits == 16 && nChannels == 2)
        nFillSize = nCopySize; // Most common
    else if (nBits == 16 && nChannels == 1)
        nFillSize = nCopySize << 1;
    else if (nBits == 8 && nChannels == 2)
        nFillSize = nCopySize << 1;
    else /*if (nBits == 8 && nChannels == 1)*/
        nFillSize = nCopySize << 2;

    return nFillSize;
}

// Convert size of bytes from hardware transfer(16 bits, 2 channels)
// to user input (8/16 bits, 1/2 channels)
INLINE GetCopySize(UInt32 nFillSize, UInt32 nBits, UInt32 nChannels)
{
    int nCopySize;

    if (nBits == 16 && nChannels == 2)
        nCopySize = nFillSize; // Most common
    else if (nBits == 16 && nChannels == 1)
        nCopySize = nFillSize >> 1;
    else if (nBits == 8 && nChannels == 2)
        nCopySize = nFillSize >> 1;
    else /*if (nBits == 8 && nChannels == 1)*/
        nCopySize = nFillSize >> 2;

    return nCopySize;
}

#define USE_SOURCE(source) \
    ((source == pCxt->aiSourceLeft) || (source == pCxt->aiSourceRight))
ECode Audio_GetState(AudioContext *pCxt, Audio_Status *pStatus)
{
    pStatus->State = pCxt->state;

    memcpy(&pStatus->AudioOutParams, &pCxt->aoParams, sizeof(Audio_Params));
    memcpy(&pStatus->AudioInParams, &pCxt->aiParams, sizeof(Audio_Params));

    return NOERROR;
}

// Touch driver will also use this.
// Because touch and audio uses the same codec.
ECode Audio_InitHardware(AudioContext *pCxt)
{
    // GPIO Settings
    Codec_SetGpios(pCxt);

    // Reset Ac97 Link
    Ac97_Reset(pCxt);

    // Init audio codec
    Codec_Init(pCxt);

    return NOERROR;
}

ECode Audio_Create(AudioContext *pCxt)
{
#if (_TEST_TYPE == 41)      /* Test for Runtime of ISR */
    memset(ulDmaChOutIsrTime, 0, sizeof(UInt32) * SAMPLETIMES);
    memset(ulDmaChRcvIsrTime, 0, sizeof(UInt32) * SAMPLETIMES);
#endif //_TEST_TYPE == 41

    Audio_InitHardware(pCxt);

    // Default volume
    pCxt->aoVolLeft = 0xa0;
    pCxt->aoVolRight = 0xa0;
    pCxt->aiVolLeft = 0xa0;
    pCxt->aiVolRight = 0xa0;
    pCxt->callVolume = 0xff;

    // Select DMA channels for playing & recording
    pCxt->aoDmaCh = Drv_DmaInstall(0, (void *)pCxt, (void *)AO_DmaIsr);
    pCxt->aiDmaCh = Drv_DmaInstall(0, (void *)pCxt, (void *)AI_DmaIsr);

    DBGOUT(DBG_LOG, ("AudioInstall, aoDmaCh = %d\n", pCxt->aoDmaCh));
    DBGOUT(DBG_LOG, ("AudioInstall, aiDmaCh = %d\n", pCxt->aiDmaCh));

    assert(pCxt->aoDmaCh >= 0 && pCxt->aoDmaCh < 32);
    assert(pCxt->aiDmaCh >= 0 && pCxt->aiDmaCh < 32);

    pCxt->state = 0;
    pCxt->auxState = 0;

    pCxt->aoBufStatus = BUFSTATUS_INITIAL;
    pCxt->aiBufStatus = BUFSTATUS_INITIAL;

    Codec_InitHeadset(pCxt);

    //
    // Init buffers
    //

    // Init audio out buffers
    //
    // Allocate multiple buffers for DMA transfer (maximal number)
    pCxt->aoBufferVirtAddr =  (Address)Drv_AllocBuffer(AUDIO_BUFCNT,
                    DRVBUF_SPEED_FAST);
    assert(pCxt->aoBufferVirtAddr && "Alloc audio buffer error!");
    pCxt->aoBufferPhysAddr = Drv_GetBufferPhysicalAddress(
                    (void *)pCxt->aoBufferVirtAddr);

    // Get DMA Descriptors
    pCxt->aoDmaDescVirtAddr = (Address)Drv_AllocBuffer(1, DRVBUF_SPEED_FAST);
    assert(pCxt->aoDmaDescVirtAddr && "Alloc audio DMA descriptors error!");
    pCxt->aoDmaDescPhysAddr = Drv_GetBufferPhysicalAddress(
                    (void *)pCxt->aoDmaDescVirtAddr);

    // Init audio in buffers
    //
#ifdef AUDIO_INOUT_ATSAMETIME
    // Allocate multiple buffers for DMA transfer (maximal number)
    pCxt->aiBufferVirtAddr =  (Address)Drv_AllocBuffer(AUDIO_BUFCNT,
                    DRVBUF_SPEED_FAST);
    assert(pCxt->aiBufferVirtAddr && "Alloc audio buffer error!");
    pCxt->aiBufferPhysAddr = Drv_GetBufferPhysicalAddress(
                    (void *)pCxt->aiBufferVirtAddr);

    // Get DMA Descriptors
    pCxt->aiDmaDescVirtAddr = (Address)Drv_AllocBuffer(1,
                    DRVBUF_SPEED_FAST);
    assert(pCxt->aiDmaDescVirtAddr && "Alloc audio DMA descriptors error!");
    pCxt->aiDmaDescPhysAddr = Drv_GetBufferPhysicalAddress(
                    (void *)pCxt->aiDmaDescVirtAddr);
#else

    // Now STATE_AUDIOOUT and STATE_AUDIOIN won't be set at the same time,
    // so audio in buffers also use audio out buffer.
    pCxt->aiBufferVirtAddr = pCxt->aoBufferVirtAddr;
    pCxt->aiBufferPhysAddr = pCxt->aoBufferPhysAddr;
    pCxt->aiDmaDescVirtAddr = pCxt->aoDmaDescVirtAddr;
    pCxt->aiDmaDescPhysAddr = pCxt->aoDmaDescPhysAddr;
#endif

    // Buffer count now using
    pCxt->aoBufCnt = 0;
    pCxt->aiBufCnt = 0;

    // SUCCESSFULLY INIT DEBUG MESSAGE
    cprintf("Initial Audio Successfully!\n");

    return NOERROR;
}

////////////////////////////////////////////////////////////////////////////////
//
// Functions for STATE_AUDIOOUT
//
static ECode AO_AllocBuffer(AudioContext *pCxt)
{
    UInt32 nBufCnt;
    // Dynamic calculate buffer count.
    // The maximal playing length in the driver buffer is about 1s.
    // And nBufCnt must be equal or less than AUDIO_BUFCNT.
    // No matter the user input format, the internal audio format.
    // For AC97 there is always 2 channels & 16bits.
    // 1s, 2 channels, 2 bytes per sample -> pCxt->nRate * 4
    nBufCnt = (pCxt->aoParams.SampleRate * 4) / AUDIO_BUFSIZE;
    if (nBufCnt > AUDIO_BUFCNT) nBufCnt = AUDIO_BUFCNT;
    pCxt->aoBufCnt = nBufCnt;

    DBGOUT(DBG_LOG, ("AO_AllocBuffer, pCxt->aoBufCnt = %d\n", pCxt->aoBufCnt));

    memset((void *)pCxt->aoDmaDescVirtAddr, 0, 1 << PAGE_SHIFT);
    memset((void *)pCxt->aoBufferVirtAddr, 0, nBufCnt << PAGE_SHIFT);
    return NOERROR;
}

static void AO_FreeBuffer(AudioContext *pCxt)
{
    DBGOUT(DBG_LOG, ("AO_FreeBuffer pCxt->aoBufCnt = %d\n", pCxt->aoBufCnt));
    pCxt->aoBufCnt = 0;
}

static UInt32 AO_FillBuffer(AudioContext *pCxt, const Byte *pBuf, int Size)
{
    Byte *pBufDes;
    UInt32 nFillSize, nCopySize, nRemainSize, nTotalCopySize = 0;

    nRemainSize = Size;

    do {
        pBufDes = (Byte *)(pCxt->aoBufferVirtAddr + AUDIO_BUFSIZE
                * pCxt->aoFillingBufIdx);

        // Remain bytes in current filling buffer
        nFillSize = AUDIO_BUFSIZE;
        nCopySize = GetCopySize(nFillSize,
            pCxt->aoParams.BitsPerSample, pCxt->aoParams.Channels);

        // Maximal copy count
        if (nCopySize > nRemainSize) {
            nCopySize = nRemainSize;
            nFillSize = GetFillSize(nCopySize,
                pCxt->aoParams.BitsPerSample, pCxt->aoParams.Channels);
            // DMA transfer size must be an integer multiple of 32 bytes
            if (nFillSize % 32) {
                nFillSize = nFillSize & (~0x1f); // Round up 32
            }
        }

        pCxt->aoBufCopyFunc(pBufDes, (Byte *)pBuf, nCopySize);
        pBuf += nCopySize;
        nTotalCopySize += nCopySize;

        FillDescriptors(pCxt, STATE_AUDIOOUT, pCxt->aoFillingBufIdx, nFillSize);

        pCxt->aoFillingBufIdx = AO_NEXT_BUFFER(pCxt->aoFillingBufIdx);

        if (pCxt->aoFillingBufIdx == pCxt->aoTransBufIdx) {
            pCxt->aoBufStatus = BUFSTATUS_FULL;
            DBGOUT(DBG_BUF, ("AO_FillBuffer, BUFSTATUS_FULL, filling idx: %d\n",
                pCxt->aoFillingBufIdx));
            break;
        }

        nRemainSize -= nCopySize;

        DBGOUT(DBG_BUF,
            ("AO_FillBuffer, remain %d, idex %d, total %d\n",
            nRemainSize, pCxt->aoFillingBufIdx,nTotalCopySize));
    } while (nRemainSize > 0);

    return nTotalCopySize;
}

ECode AO_OpenHardware(AudioContext *pCxt)
{
    ECode ec;

    pCxt->auxState &= ~AUXSTATE_HEADSETPLUGISR; // Clear this flag if it set

    Codec_OpenAc97DAC(pCxt);
    ec = Codec_SetSampleRate(pCxt->aoParams.SampleRate, STATE_AUDIOOUT);
    if (FAILED(ec)) goto Exit;

    pCxt->aoFillingBufIdx = 0;
    pCxt->aoTransBufIdx = 0;
    pCxt->aoBufStatus = BUFSTATUS_NORMAL;

    pCxt->auxState &= ~(AUXSTATE_SPEAKER_WORKING | AUXSTATE_HEADSETOUT_WORKING
                        | AUXSTATE_BLUETOOTHOUT_WORKING);

Exit:
    DBGOUT(DBG_LOG, ("AO_OpenHardware, ec = 0x%x, state = 0x%x\n",
        ec, pCxt->state));
    DBGOUT_FUNC(DBG_DUMP, DumpAC97Regs());
    return ec;
}

ECode AO_Open(AudioContext *pCxt, Audio_Params *pParams)
{
    ECode ec;

    // Initializing audio out device (STATE_AUDIOOUT)
    // 1. If the basic state is STATE_CALL,
    //    Don't do anything and return!
    // 2. If the basic state is STATE_AUDIOOUT or STATE_AUDIOIN,
    //    Release it now!
    if (pCxt->state & STATE_CALL) {
        DBGOUT(DBG_LOG, ("AO_Open, return error when in call mode."));
        ec = E_ACCESS_DENIED;
        goto Exit;
    }
    else if (pCxt->state & STATE_AUDIOOUT) {
        DBGOUT(DBG_LOG, ("AO_Open, release previous playing."));
        AO_Close(pCxt);
    }
#ifndef AUDIO_INOUT_ATSAMETIME
    else if (pCxt->state & STATE_AUDIOIN) {
        DBGOUT(DBG_LOG, ("AO_Open, release previous recording."));
        AI_Close(pCxt);
    }
#endif

    memcpy(&pCxt->aoParams, pParams, sizeof(Audio_Params));

    AO_AllocBuffer(pCxt);

    // Adjust basic information
    if (pCxt->aoParams.BitsPerSample != 8 &&
        pCxt->aoParams.BitsPerSample != 16)
        pCxt->aoParams.BitsPerSample = 16;
    if (pCxt->aoParams.Channels != 1 &&
        pCxt->aoParams.Channels != 2)
        pCxt->aoParams.Channels = 1;

    // Add callback functions for memory copy
    if (1 == pCxt->aoParams.Channels) {
        if (pCxt->aoParams.BitsPerSample == 16) {
            pCxt->aoBufCopyFunc = memcpy_m16_s16;
        }
        else {
            pCxt->aoBufCopyFunc = memcpy_m8_s16;
        }
    }
    else {
        if (pCxt->aoParams.BitsPerSample == 16) {
            pCxt->aoBufCopyFunc = memcpy_s16_s16;
        }
        else {
            pCxt->aoBufCopyFunc = memcpy_s8_s16;
        }
    }

    ec = AO_OpenHardware(pCxt);
    if (SUCCEEDED(ec)) {
        pCxt->state &= ~POWER_CLOSE;
        pCxt->state |= STATE_AUDIOOUT; // AO_OpenHardware will need this flag
        AO_SetVolume(pCxt, pCxt->aoVolLeft, pCxt->aoVolRight);
    }

Exit:
    DBGOUT(DBG_LOG, ("AO_Open: Rate=%d, BitsPerSample=%d, Channels=%d\n",
        pParams->SampleRate, pParams->BitsPerSample, pParams->Channels));
    DBGOUT(DBG_LOG, ("AO_Open: ec = 0x%x, state = 0x%x\n", ec, pCxt->state));
    DBGOUT_FUNC(DBG_DUMP, DumpAC97Regs());

    return ec;
}

void AO_CloseHardware(AudioContext *pCxt)
{
    if (pCxt->aoDmaInProgess) {
        pCxt->aoDmaInProgess = FALSE;
        StopDmac(pCxt->aoDmaCh);
    }

    // Don't close codec right now in order to avoid pops and clicks
    //AO_SetVolume(pCxt, 0, 0);

    DBGOUT(DBG_LOG, ("AO_CloseHardware, state = 0x%x\n", pCxt->state));
}

// Close audio out
void AO_Close(AudioContext *pCxt)
{
    if (!(pCxt->state & STATE_AUDIOOUT)) return;

    AO_FlushBuffer(pCxt);

    // Mute all audio out device
    Codec_SetVolume(pCxt, 0, 0, VOLUMETYPE_SPEAKER);
    Codec_SetVolume(pCxt, 0, 0, VOLUMETYPE_HEADSET);

    AO_CloseHardware(pCxt);

    AO_FreeBuffer(pCxt);

    pCxt->state &= ~STATE_AUDIOOUT;

    DBGOUT(DBG_LOG, ("AO_Close, state = 0x%x\n", pCxt->state));
}

void AO_Reset(AudioContext *pCxt)
{
    if (pCxt->aoDmaInProgess) {
        pCxt->aoDmaInProgess = FALSE;
        StopDmac(pCxt->aoDmaCh);
    }
    AO_OpenHardware(pCxt);
}

ECode AO_SetVolume(AudioContext *pCxt, Byte left, Byte right)
{
    ECode ec = NOERROR;

    pCxt->aoVolLeft = left;
    pCxt->aoVolRight = right;
    // If it isn't playing now, only change the volume values
    if (!(pCxt->state & STATE_AUDIOOUT)) {
        goto Exit;
    }

    if (pCxt->state & AUDIOOUT_SPEAKER) {
        ec = Codec_SetVolume(pCxt, left, right, VOLUMETYPE_SPEAKER);
        if (FAILED(ec)) goto Exit;
    }

    Codec_GetHeadsetStatus(pCxt);
    if (pCxt->state & AUDIOOUT_HEADSET) {
        ec = Codec_SetVolume(pCxt, left, right, VOLUMETYPE_HEADSET);
    }

#ifdef BLUETOOTH_PLAY_RECORD_ENABLE
    if (pCxt->state & AUDIOOUT_BLUETOOTH) {
        ec = Codec_SetVolume(pCxt, left, right, VOLUMETYPE_BTHEADSET);
    }
#endif

Exit:
    DBGOUT(DBG_LOG, ("AO_SetVolume, left: 0x%x, right: 0x%x\n", left, right));
    DBGOUT(DBG_LOG, ("AO_SetVolume, ec = 0x%x, state = 0x%x\n", ec, pCxt->state));
    DBGOUT_FUNC(DBG_DUMP, DumpAC97Regs());

    return NOERROR;
}

void AO_GetVolume(AudioContext *pCxt, Byte *pLeft, Byte *pRight)
{
    *pLeft = pCxt->aoVolLeft;
    *pRight = pCxt->aoVolRight;
}

ECode AO_FlushBuffer(AudioContext *pCxt)
{
    if ((pCxt->state & STATE_AUDIOOUT) && pCxt->aoDmaInProgess) {
        int remainBufCnt, sleepTime;

        // NOTE: The buffers make a loop
        if (pCxt->aoFillingBufIdx > pCxt->aoTransBufIdx) {
            remainBufCnt = pCxt->aoFillingBufIdx - pCxt->aoTransBufIdx;
        }
        else {
            remainBufCnt = pCxt->aoFillingBufIdx +
                pCxt->aoBufCnt - pCxt->aoTransBufIdx;
        }

        // Sleep some time, until the DMA buffers are all transfered.
        // The time is a approximate value.
        // For AC97 there is always 2 channels & 16bits.
        // 2 channels, 2 bytes per sample -> pCxt->nRate * 4
        sleepTime = remainBufCnt * AUDIO_BUFSIZE  * 100 /
                    (pCxt->aoParams.SampleRate * 4);
        DzSleep(sleepTime, NULL);
    }

    return NOERROR;
}

ECode AO_Write(AudioContext *pCxt,
    void *pData, UInt32 dataLen, UInt32 * pWritten)
{
    if (!(pCxt->state & STATE_AUDIOOUT)) {
        DBGOUT(DBG_ERR, ("AO_Write, can't operate now, state: 0x%x\n",
            pCxt->state));
        return E_ACCESS_DENIED;
    }

    // Deal with headset volume when headset is plugged in or out
    if (pCxt->auxState & AUXSTATE_HEADSETPLUGISR) {
        DBGOUT(DBG_LOG, ("AO_Write, deal with headset, state: 0x%x, "\
            "auxState: 0x%x\n", pCxt->state, pCxt->auxState));

        pCxt->auxState &= ~AUXSTATE_HEADSETPLUGISR;
        if ((pCxt->auxState & AUXSTATE_HEADSETPLUG)) {
            if (pCxt->state & AUDIOOUT_HEADSET) {
                Codec_SetVolume(pCxt, pCxt->aoVolLeft,
                    pCxt->aoVolRight, VOLUMETYPE_HEADSET);
            }
        }
        else {
            Codec_SetVolume(pCxt, 0, 0, VOLUMETYPE_HEADSET);
        }
    }

    s_syncAudioOut.Clear();
    if (pCxt->aoDmaInProgess && pCxt->aoBufStatus == BUFSTATUS_FULL) {
        WaitResult wr;
        s_syncAudioOut.TryWait(MAX_WAIT_TIME, &wr, NULL);

        if (WaitResult_TimedOut == wr) {
            // No buffers to use after waiting, perhaps the device isn't open.
            DBGOUT(DBG_ERR, ("AO_Write, Time is out. trans idx = %d, "\
                "filling idx = %d\n",
                pCxt->aoTransBufIdx, pCxt->aoFillingBufIdx));
            return E_TIMED_OUT;
        }
    }

    if (!pCxt->aoDmaInProgess) {
        pCxt->aoFillingBufIdx = 0;
        pCxt->aoTransBufIdx = 0;

        INTC_DMAC_INT_EN(ICMR);
    }

    *pWritten = AO_FillBuffer(pCxt, (byte*)pData, dataLen);

    // If DMA transfer doens't start, just do it!
    if (!pCxt->aoDmaInProgess) {
        pCxt->aoDmaInProgess = TRUE;
        InitDMAC(pCxt, pCxt->aoDmaCh);
    }

    return NOERROR;
}

Boolean AO_DmaIsr(AudioContext *pCxt, PBoolean pWakeUp)
{
#if (_TEST_TYPE == 41)
    UInt32 uTimerStart = OSCR0;
#endif //_TEST_TYPE == 41

    //if the out channel stopped at the end of a buffer xfer
    if (DCSR(pCxt->aoDmaCh) & DCSR_ENDINTR) {
        DCSR(pCxt->aoDmaCh) |= DCSR_ENDINTR;

        pCxt->aoTransBufIdx = AO_NEXT_BUFFER(pCxt->aoTransBufIdx);

        if (pCxt->aoBufStatus == BUFSTATUS_FULL) {
            pCxt->aoBufStatus = BUFSTATUS_NORMAL;
            DBGOUT(DBG_ISR,
                ("AO_DmaIsr, buf state: full->normal, next buf: %d\n",
                pCxt->aoTransBufIdx));
            s_syncAudioOut.NotifyByIsr();
        }
        else if (pCxt->aoTransBufIdx == pCxt->aoFillingBufIdx) {
            DBGOUT(DBG_ISR, ("AO_DmaIsr, buffer state: empty\n"));
            StopDmac(pCxt->aoDmaCh);
            pCxt->aoBufStatus = BUFSTATUS_EMPTY;
            pCxt->aoDmaInProgess = FALSE;
        }
    }
    else if (DCSR(pCxt->aoDmaCh) & DCSR_BUSERRINTR) {
        DBGOUT(DBG_ERR, ("AO_DmaIsr, bus error!\n", pCxt->aoDmaCh));
        DCSR(pCxt->aoDmaCh) |= DCSR_BUSERRINTR;
        StopDmac(pCxt->aoDmaCh);
        pCxt->aoBufStatus = BUFSTATUS_INITIAL;
        pCxt->aoDmaInProgess = FALSE;
    }

#if (_TEST_TYPE == 41)
    UInt32 uTimerEnd = OSCR0;
    if (nDmaChOutIsrCounter >= SAMPLETIMES) nDmaChOutIsrCounter = 0;
    ulDmaChOutIsrTime[nDmaChOutIsrCounter++] = \
                            (uTimerEnd >= uTimerStart) ? \
                            (uTimerEnd - uTimerStart) : \
                            ((UInt32)0xFFFFFFFF - uTimerStart + uTimerEnd);

#endif //_TEST_TYPE == 41

    return TRUE;
}

ECode AO_SelectDevice(AudioContext *pCxt, UInt32 device)
{
    int volumeType;
    ECode ec = NOERROR;

    DBGOUT(DBG_LOG, ("AO_SelectDevice, device = %d\n", device));

#ifdef HEADSET_AUTOOPEN
    if (AODEVICE_HEADSET == device) return NOERROR;
#endif

    switch ((int)device) {
        case AODEVICE_SPEAKER:
            pCxt->state |= AUDIOOUT_SPEAKER;
            volumeType = VOLUMETYPE_SPEAKER;
            break;

        case AODEVICE_HEADSET:
            if (pCxt->auxState & AUXSTATE_HEADSETPLUG) {

                pCxt->state |= AUDIOOUT_HEADSET;

                volumeType = VOLUMETYPE_HEADSET;
            }
            else {
                DBGOUT(DBG_ERR, ("AO_SelectDevice, Can't set headset "\
                    "output volume when it isn't plugged.\n"));
                ec = E_ACCESS_DENIED;
                goto Exit;
            }
            break;

#ifdef BLUETOOTH_PLAY_RECORD_ENABLE
        case AODEVICE_BTHEADSET:
            pCxt->state |= AUDIOOUT_BLUETOOTH;
            volumeType = VOLUMETYPE_BTHEADSET;
            break;
#endif

        default:
            ec = E_ACCESS_DENIED;
            goto Exit;
    }

    if (pCxt->state & STATE_AUDIOOUT) {
        ec = Codec_SetVolume(pCxt, pCxt->aoVolLeft,
            pCxt->aoVolRight, volumeType);
    }

Exit:
    return ec;
}

ECode AO_CloseDevice(AudioContext *pCxt, UInt32 device)
{
    int volumeType;
    ECode ec = NOERROR;

    DBGOUT(DBG_LOG, ("AO_CloseDevice, device = %d\n", device));

#ifdef HEADSET_AUTOOPEN
    if (AODEVICE_HEADSET == device) return ec;
#endif

    switch ((int)device) {
        case AODEVICE_SPEAKER:
            pCxt->state &= ~AUDIOOUT_SPEAKER;
            volumeType = VOLUMETYPE_SPEAKER;
            break;

        case AODEVICE_HEADSET:
            pCxt->state &= ~AUDIOOUT_HEADSET;
            volumeType = VOLUMETYPE_HEADSET;
            break;

        case AODEVICE_BTHEADSET:
            pCxt->state &= ~AUDIOOUT_BLUETOOTH;
            volumeType = VOLUMETYPE_MONO;
            break;

        default:
            return E_ACCESS_DENIED;
    }

    if (pCxt->state & STATE_AUDIOOUT) {
        ec = Codec_SetVolume(pCxt, 0, 0, volumeType);
    }
    if (AODEVICE_SPEAKER == device) {
        Codec_OpenAmplifier(pCxt, FALSE);
    }

    return ec;
}

////////////////////////////////////////////////////////////////////////////////
//
// Functions for STATE_AUDIOOUT
//
static ECode AI_AllocBuffer(AudioContext *pCxt)
{
    UInt32 nBufCnt;
    // Dynamic calculate buffer count.
    // The maximal playing length in the driver buffer is about 1s.
    // And nBufCnt must be equal or less than AUDIO_BUFCNT.
    // No matter the user input format, the internal audio format.
    // For AC97 there is always 2 channels & 16bits.
    // 1s, 2 channels, 2 bytes per sample -> pCxt->nRate * 4
    nBufCnt = (pCxt->aiParams.SampleRate * 4) / AUDIO_BUFSIZE;
    if (nBufCnt > AUDIO_BUFCNT) nBufCnt = AUDIO_BUFCNT;
    pCxt->aiBufCnt = nBufCnt;

    DBGOUT(DBG_LOG, ("AI_AllocBuffer pCxt->aiBufCnt = %d\n", pCxt->aiBufCnt));

    memset((void *)pCxt->aiDmaDescVirtAddr, 0, 1 << PAGE_SHIFT);
    memset((void *)pCxt->aiBufferVirtAddr, 0, nBufCnt << PAGE_SHIFT);
    return NOERROR;
}

static void AI_FreeBuffer(AudioContext *pCxt)
{
    DBGOUT(DBG_LOG, ("AI_FreeBuffer pCxt->aiBufCnt = %d\n", pCxt->aiBufCnt));
    pCxt->aiBufCnt = 0;
}

static UInt32 AI_GetBuffer(AudioContext *pCxt, Byte *pBuf, int Size)
{
    Byte *pBufSrc;
    UInt32 nCanCopySize, nCopySize, nRemainSize, nTotalCopySize = 0;

    nRemainSize = Size;

    do {
        // Remain bytes in current filling buffer
        nCanCopySize = GetCopySize(AUDIO_BUFSIZE - pCxt->aiGettingBufPos,
                pCxt->aiParams.BitsPerSample, pCxt->aiParams.Channels);

        // Maximal copy count
        if (nCanCopySize > nRemainSize)
            nCopySize = nRemainSize;
        else
            nCopySize = nCanCopySize;

        pBufSrc = (Byte *)(pCxt->aiBufferVirtAddr +
                AUDIO_BUFSIZE * pCxt->aiGettingBufIdx + pCxt->aiGettingBufPos);
        pCxt->aiBufCopyFunc(pBuf, pBufSrc, nCopySize);
        pBuf += nCopySize;
        nTotalCopySize += nCopySize;

        if (nCanCopySize <= nRemainSize) {
            pCxt->aiGettingBufIdx = AI_NEXT_BUFFER(pCxt->aiGettingBufIdx);
            pCxt->aiGettingBufPos = 0;
        }
        else {
            pCxt->aiGettingBufPos += GetFillSize(nCopySize,
                pCxt->aiParams.BitsPerSample, pCxt->aiParams.Channels);
        }

        if (pCxt->aiGettingBufIdx == pCxt->aiTransBufIdx) {
            pCxt->aiBufStatus = BUFSTATUS_EMPTY;
            DBGOUT(DBG_BUF, ("AI_GetBuffer, BUFSTATUS_EMPTY, Getting idx: %d\n",
                pCxt->aiGettingBufIdx));
            break;
        }

        nRemainSize -= nCopySize;
        DBGOUT(DBG_BUF, ("AI_GetBuffer remain %d, getting idx %d,total %d\n",
            nRemainSize,pCxt->aiGettingBufIdx, nTotalCopySize));
    } while (nRemainSize > 0);

    return nTotalCopySize;
}

ECode AI_OpenHardware(AudioContext *pCxt)
{
    ECode ec;

    pCxt->auxState &= ~AUXSTATE_HEADSETPLUGISR; // Clear this flag if it set

    Codec_OpenAc97ADC(pCxt);
    ec = Codec_SetSampleRate(pCxt->aiParams.SampleRate, STATE_AUDIOIN);
    if (FAILED(ec)) goto Exit;

    ec = Codec_SetAudioInSource(pCxt, AICHANNEL_LEFT,
        AUDIOIN_GETLEFTSOURCE(pCxt->state));
    if (FAILED(ec)) goto Exit;

    if (pCxt->aiParams.Channels > 1) {
        ec = Codec_SetAudioInSource(pCxt, AICHANNEL_RIGHT,
            AUDIOIN_GETRIGHTSOURCE(pCxt->state));
        if (FAILED(ec)) goto Exit;
    }

    pCxt->aiGettingBufPos = 0;
    pCxt->aiGettingBufIdx = 0;
    pCxt->aiTransBufIdx = 0;
    pCxt->aiBufStatus = BUFSTATUS_NORMAL;

Exit:
    DBGOUT(DBG_LOG, ("AI_OpenHardware, ec = 0x%x, state = 0x%x\n",
        ec, pCxt->state));
    DBGOUT_FUNC(DBG_DUMP, DumpAC97Regs());
    return ec;
}

ECode AI_Open(AudioContext *pCxt, Audio_Params *pParams)
{
    ECode ec;

    // Initializing audio out device (STATE_AUDIOOUT)
    // 1. If the basic state is STATE_CALL, don't bother it and go on setting,
    //    Except bluetooth is using.
    // 2. If the basic state is STATE_AUDIOOUT or STATE_AUDIOIN,
    //    Release it now!
    if (pCxt->state & STATE_CALL) {
        if (USING_BLUETOOTH(pCxt->state)) {
            DBGOUT(DBG_LOG, ("AI_Open, return error in call mode"\
                " when bluetooth is using."));
            ec = E_ACCESS_DENIED;
            goto Exit;
        }
    }
#ifndef AUDIO_INOUT_ATSAMETIME
    else if (pCxt->state & STATE_AUDIOOUT) {
        DBGOUT(DBG_LOG, ("AI_Open, release previous playing."));
        AO_Close(pCxt);
    }
#endif
    if (pCxt->state & STATE_AUDIOIN) {
        DBGOUT(DBG_LOG, ("AI_Open, release previous recording."));
        AI_Close(pCxt);
    }

    memcpy(&pCxt->aiParams, pParams, sizeof(Audio_Params));

    AI_AllocBuffer(pCxt);

    for (UInt32 i = 0; i < pCxt->aiBufCnt; i++) {
        FillDescriptors(pCxt, STATE_AUDIOIN, i, AUDIO_BUFSIZE);
    }

    // Adjust basic information
    if (pCxt->aiParams.BitsPerSample != 8 &&
        pCxt->aiParams.BitsPerSample != 16)
        pCxt->aiParams.BitsPerSample = 16;
    if (pCxt->aiParams.Channels != 1 &&
        pCxt->aiParams.Channels != 2)
        pCxt->aiParams.Channels = 1;

    // Add callback functions for memory copy
    if (1 == pCxt->aiParams.Channels) {
        if (pCxt->aiParams.BitsPerSample == 16) {
            pCxt->aiBufCopyFunc = memcpy_s16_m16;
        }
        else {
            pCxt->aiBufCopyFunc = memcpy_s16_m8;
        }
    }
    else {
        if (pCxt->aiParams.BitsPerSample == 16) {
            pCxt->aiBufCopyFunc = memcpy_s16_s16;
        }
        else {
            pCxt->aiBufCopyFunc = memcpy_s16_s8;
        }
    }

    ec = AI_OpenHardware(pCxt);
    if (SUCCEEDED(ec)) {
        pCxt->state &= ~POWER_CLOSE;
        pCxt->state |= STATE_AUDIOIN;
    }

Exit:
    DBGOUT(DBG_LOG, ("AI_Open: Rate=%d, BitsPerSample=%d, Channels=%d\n",
        pParams->SampleRate, pParams->BitsPerSample, pParams->Channels));
    DBGOUT(DBG_LOG, ("AI_Open: ec = 0x%x, state = 0x%x\n", ec, pCxt->state));

    return ec;
}

void AI_CloseHardware(AudioContext *pCxt)
{
    if (pCxt->aiDmaInProgess) {
        pCxt->aiDmaInProgess = FALSE;
        StopDmac(pCxt->aiDmaCh);
    }

    Codec_SetAudioInSource(pCxt, AICHANNEL_CLOSE, AICHANNEL_CLOSE);

    DBGOUT(DBG_LOG, ("AI_CloseHardware, state = 0x%x\n", pCxt->state));
}

// Close audio in
void AI_Close(AudioContext *pCxt)
{
    if (!(pCxt->state & STATE_AUDIOIN)) return;

    AI_CloseHardware(pCxt);

    AI_FreeBuffer(pCxt);
    pCxt->state &= ~STATE_AUDIOIN;

    DBGOUT(DBG_LOG, ("AI_Close, state = 0x%x\n", pCxt->state));
}

void AI_Reset(AudioContext *pCxt)
{
    if (pCxt->aiDmaInProgess) {
        pCxt->aiDmaInProgess = FALSE;
        StopDmac(pCxt->aiDmaCh);
    }
    AI_OpenHardware(pCxt);
}

ECode AI_SetVolume(AudioContext *pCxt, Byte left, Byte right)
{
    ECode ec = NOERROR;

    pCxt->aiVolLeft = left;
    pCxt->aiVolRight = right;
    // If it isn't recording now, only change the volume values
    if (!(pCxt->state & STATE_AUDIOIN)) {
        goto Exit;
    }

    ec = Codec_SetRecordVolume(pCxt, pCxt->aiVolLeft, pCxt->aiVolRight);

Exit:
    DBGOUT(DBG_LOG, ("AI_SetVolume, left: 0x%x, right: 0x%x\n", left, right));
    DBGOUT(DBG_LOG,
        ("AI_SetVolume, ec = 0x%x, state = 0x%x\n", ec, pCxt->state));
    DBGOUT_FUNC(DBG_DUMP, DumpAC97Regs());

    return ec;
}

void AI_GetVolume(AudioContext *pCxt, Byte *pLeft, Byte *pRight)
{
    *pLeft = pCxt->aiVolLeft;
    *pRight = pCxt->aiVolRight;
}

ECode AI_Read(AudioContext *pCxt,
    void *pData, UInt32 dataLen, UInt32 * pRead)
{
    if (!(pCxt->state & STATE_AUDIOIN)) {
        DBGOUT(DBG_ERR, ("AI_Read, can't operate now, state: 0x%x\n",
            pCxt->state));
        return E_ACCESS_DENIED;
    }

    // Deal with recording when headset is plugged in or out
    if (((AUDIOIN_HEADSET == AUDIOIN_GETLEFTSOURCE(pCxt->state))
        || (AUDIOIN_HEADSET == AUDIOIN_GETRIGHTSOURCE(pCxt->state)))
        && (!(pCxt->auxState & AUXSTATE_HEADSETPLUG))
        ) {
        DBGOUT(DBG_ERR, ("AI_Read, Can't continue recording without MIC "\
            " on headset.\n"));
        return E_ACCESS_DENIED;
    }

    // If DMA transfer doens't start, just do it!
    if (!pCxt->aiDmaInProgess) {
        pCxt->aiDmaInProgess = TRUE;
        pCxt->aiBufStatus = BUFSTATUS_EMPTY;
        INTC_DMAC_INT_EN(ICMR);
        InitDMAC(pCxt, pCxt->aiDmaCh);
    }

    s_syncAudioIn.Clear();

    if (/*pCxt->aoDmaInProgess && */pCxt->aiBufStatus == BUFSTATUS_EMPTY) {
        // No buffer to read, so should wait here
        DBGOUT(DBG_ISR, ("AI_Read, Here wait to get a buffer.\n"));
        // Wait here , the max time is MAX_WAIT_TIME ms
        WaitResult wr;
        s_syncAudioIn.TryWait(MAX_WAIT_TIME, &wr, NULL);

        if (WaitResult_TimedOut == wr) {
            // No buffers to use after waiting, perhaps the device isn't open.
            DBGOUT(DBG_ERR, ("AI_Read, Time is out. transfer idx = %d,"\
                " getting idx = %d\n",
                pCxt->aiTransBufIdx, pCxt->aiGettingBufIdx));

            return E_TIMED_OUT;
        }
    }

    *pRead = AI_GetBuffer(pCxt, (Byte *)pData, dataLen);

    return NOERROR;
}

Boolean AI_DmaIsr(AudioContext *pCxt, PBoolean pWakeUp)
{
#if (_TEST_TYPE == 41)
    UInt32 uTimerStart = OSCR0;
#endif //_TEST_TYPE == 41

    //if the in channel stopped at the end of a buffer xfer
    if (DCSR(pCxt->aiDmaCh) & DCSR_ENDINTR) {
        DCSR(pCxt->aiDmaCh) |= DCSR_ENDINTR;
        pCxt->aiTransBufIdx = AI_NEXT_BUFFER(pCxt->aiTransBufIdx);
        if (pCxt->aiBufStatus == BUFSTATUS_EMPTY) {
            pCxt->aiBufStatus = BUFSTATUS_NORMAL;
            DBGOUT(DBG_ISR,
                ("AI_DmaIsr, buf state: empty->normal, next buf: %d\n",
                pCxt->aiTransBufIdx));
            s_syncAudioIn.NotifyByIsr();
        }
        else if (pCxt->aiTransBufIdx == pCxt->aiGettingBufIdx) {
            StopDmac(pCxt->aiDmaCh);
            DBGOUT(DBG_ISR, ("AI_DmaIsr, buffer state: full\n"));
            pCxt->aiBufStatus = BUFSTATUS_FULL;
            pCxt->aiDmaInProgess = FALSE;
        }
    }
    else if (DCSR(pCxt->aiDmaCh) & DCSR_BUSERRINTR) {
        DBGOUT(DBG_ERR, ("AI_DmaIsr, bus error!\n", pCxt->aiDmaCh));
        DCSR(pCxt->aiDmaCh) |= DCSR_BUSERRINTR;
        StopDmac(pCxt->aiDmaCh);
        pCxt->aiBufStatus = BUFSTATUS_INITIAL;
        pCxt->aiDmaInProgess = FALSE;
    }

#if (_TEST_TYPE == 41)
    UInt32 uTimerEnd = OSCR0;
    if (nDmaChRcvIsrCounter >= SAMPLETIMES) nDmaChRcvIsrCounter = 0;
    ulDmaChRcvIsrTime[nDmaChRcvIsrCounter++] = \
                            (uTimerEnd >= uTimerStart) ? \
                            (uTimerEnd - uTimerStart) : \
                            ((UInt32)0xFFFFFFFF - uTimerStart + uTimerEnd);
#endif //_TEST_TYPE == 41

    return TRUE;
}

// BUGBUG: This function doesn't check the correctness of params
//   because there are so many combinations.
//   User applications must make the recording source right!
ECode AI_SelectSource(AudioContext *pCxt, Byte left, Byte right)
{
    ECode ec;

    DBGOUT(DBG_LOG, ("AI_SelectSource, left: 0x%x, right: 0x%x\n", left, right));

    // Clear old audio in source and set new audio in source
    pCxt->state &= ~AUDIOIN_SETLEFTSOURCE(AUDIOIN_SOURCEMASK);
    pCxt->state |= AUDIOIN_SETLEFTSOURCE(left);

    pCxt->state &= ~AUDIOIN_SETRIGHTSOURCE(AUDIOIN_SOURCEMASK);
    pCxt->state |= AUDIOIN_SETRIGHTSOURCE(right);

    // If it isn't recording now, only change the source values
    if (!(pCxt->state & STATE_AUDIOIN)) return NOERROR;

    ec = Codec_SetAudioInSource(pCxt, AICHANNEL_LEFT, left);
    if (FAILED(ec))return ec;

    if (pCxt->aiParams.Channels > 1) {
        ec = Codec_SetAudioInSource(pCxt, AICHANNEL_RIGHT, right);
    }

    return ec;
}

////////////////////////////////////////////////////////////////////////////////
//
// Functions for STATE_CALL
//
static ECode Call_OpenNormalMode(AudioContext *pCxt)
{
    ECode ec;

    ec = Codec_OpenNormalCall(pCxt);
    if (SUCCEEDED(ec)) {
        pCxt->state &= ~CALL_SETMODE(CALL_MODEMASK); // Clear old mode state
        pCxt->state |= CALL_SETMODE(CALL_NORMAL); // Set new mode here
    }

    DBGOUT(DBG_LOG,
        ("Call_OpenNormalMode: pCxt->state = 0x%x\n", pCxt->state));

    return ec;
}

static void Call_CloseNormalMode(AudioContext *pCxt)
{
    Codec_CloseNormalCall(pCxt);

    DBGOUT(DBG_LOG, ("Call_CloseNormalMode, state = 0x%x\n", pCxt->state));
}

static ECode Call_OpenHandFreeMode(AudioContext *pCxt)
{
    ECode ec;

    ec = Codec_OpenHandFreeCall(pCxt);
    if (SUCCEEDED(ec)) {
        pCxt->state &= ~CALL_SETMODE(CALL_MODEMASK); // Clear old mode state
        pCxt->state |= CALL_SETMODE(CALL_HANDFREE); // Set new mode here
    }

    DBGOUT(DBG_LOG,
        ("Call_OpenHandFreeMode: pCxt->state = 0x%x\n", pCxt->state));

    return ec;
}

static void Call_CloseHandFreeMode(AudioContext *pCxt)
{
    Codec_CloseHandFreeCall(pCxt);

    DBGOUT(DBG_LOG, ("Call_CloseHandFreeMode, state = 0x%x\n", pCxt->state));
}

static ECode Call_OpenBluetoothMode(AudioContext *pCxt)
{
    ECode ec;

    ec = Codec_OpenBluetoothCall(pCxt);
    if (SUCCEEDED(ec)) {
        pCxt->state &= ~CALL_SETMODE(CALL_MODEMASK); // Clear old mode state
        pCxt->state |= CALL_SETMODE(CALL_BLUETOOTH); // Set new mode here
        // Set default bluetooth headset volume
        // Codec_SetVolume(pCxt, 0xff, 0xff, VOLUMETYPE_BTHEADSET);
    }

    DBGOUT(DBG_LOG,
        ("Call_OpenBluetoothMode:  pCxt->state = 0x%x\n", pCxt->state));

    return ec;
}

static void Call_CloseBluetoothMode(AudioContext *pCxt)
{
    // Codec_SetVolume(pCxt, 0, 0, VOLUMETYPE_BTHEADSET);
    Codec_CloseBluetoothCall(pCxt);

    DBGOUT(DBG_LOG, ("Call_CloseBluetoothMode, state = 0x%x\n", pCxt->state));
}

static ECode Call_OpenHeadsetMode(AudioContext *pCxt)
{
    ECode ec;

    ec = Codec_OpenHeadsetCall(pCxt);
    if (SUCCEEDED(ec)) {
        pCxt->state &= ~CALL_SETMODE(CALL_MODEMASK); // Clear old mode state
        pCxt->state |= CALL_SETMODE(CALL_HEADSET); // Set new mode here
    }

    DBGOUT(DBG_LOG,
        ("Call_OpenHeadsetMode:  pCxt->state = 0x%x\n", pCxt->state));

    return ec;
}

static void Call_CloseHeadsetMode(AudioContext *pCxt)
{
    Codec_CloseHeadsetCall(pCxt);

    DBGOUT(DBG_LOG, ("Call_CloseHeadsetMode, state = 0x%x\n", pCxt->state));
}

static void Call_CloseCurrentCall(AudioContext *pCxt)
{
    if (pCxt->state & STATE_CALL) {
        UInt32 callMode = CALL_GETMODE(pCxt->state);
        DBGOUT(DBG_LOG, ("Call_CloseCurrentCall,  callMode = %d\n", callMode));
        switch (callMode) {
            case CALL_NORMAL:
                Call_CloseNormalMode(pCxt);
                break;
            case CALL_HANDFREE:
                Call_CloseHandFreeMode(pCxt);
                break;
            case CALL_HEADSET:
                Call_CloseHeadsetMode(pCxt);
                break;
            case CALL_BLUETOOTH:
                Call_CloseBluetoothMode(pCxt);
                break;
            default:
                break;
        }
    }
}

ECode Call_Open(AudioContext *pCxt, UInt32 callMode)
{
    ECode ec = NOERROR;

    // First, if current mode also belongs STATE_CALL, close it!
    // if STATE_AUDIOOUT, close it!
    // if STATE_AUDIOIN, don't bother it except using bluetooth.
    Call_CloseCurrentCall(pCxt);
    Ac97_WarmReset(pCxt, AUXSTATE_ACLINK_AUDIO); // Avoiding AC97 LINK closed
    if (pCxt->state & STATE_AUDIOOUT) {
        DBGOUT(DBG_LOG, ("Call_Open, release previous playing."));
        AO_Close(pCxt);
    }
    else if (pCxt->state & STATE_AUDIOIN) {
        if (USING_BLUETOOTH(pCxt->state)) {
            DBGOUT(DBG_LOG, ("Call_Open, release previous recording "\
                "when using bluetooth."));
            AI_Close(pCxt);
        }
    }

    if (callMode != CALL_HANDFREE) {
        // Close speaker when calling, but hand free mode.
        Codec_OpenAmplifier(pCxt, FALSE);
    }

    // Second, Set new call mode
    switch (callMode) {
        case CALL_NORMAL:
            ec = Call_OpenNormalMode(pCxt);
            break;
        case CALL_HANDFREE:
            ec = Call_OpenHandFreeMode(pCxt);
            break;
        case CALL_HEADSET:
            ec = Call_OpenHeadsetMode(pCxt);
            break;
        case CALL_BLUETOOTH:
            ec = Call_OpenBluetoothMode(pCxt);
            break;
        default:
            break;
    }

    if (SUCCEEDED(ec)) pCxt->state |= STATE_CALL;

    ec = Call_SetVolume(pCxt, pCxt->callVolume);

    DBGOUT(DBG_LOG,
        ("Call_Open:  ec = 0x%x, pCxt->state = 0x%x\n", ec, pCxt->state));
    DBGOUT_FUNC(DBG_DUMP, DumpAC97Regs());

    return ec;
}

void Call_Close(AudioContext *pCxt)
{
    Call_CloseCurrentCall(pCxt);
    pCxt->state &= ~STATE_CALL;

    DBGOUT(DBG_LOG, ("Call_Close, state = 0x%x\n", pCxt->state));
}

ECode Call_SetVolume(AudioContext *pCxt, Byte volume)
{
    ECode ec = NOERROR;
    pCxt->callVolume = volume;

    if (pCxt->state & STATE_CALL) {
        ec = Codec_SetCallVolume(pCxt, volume);
    }

    return ec;
}

////////////////////////////////////////////////////////////////////////////////
//
// Power management code
// These codes are mainly for STATE_AUDIOOUT and STATE_AUDIOIN
// CALL_HANDFREE and CALL_BLUETOOTH control power themselves
//
void Power_Open(AudioContext *pCxt)
{
    if (pCxt->state & STATE_AUDIOOUT) {
        AO_OpenHardware(pCxt);
        AO_SetVolume(pCxt, pCxt->aoVolLeft, pCxt->aoVolRight);
    }
    else if (pCxt->state & STATE_AUDIOIN) {
        AI_OpenHardware(pCxt);
    }

    pCxt->state &= ~POWER_CLOSE;
}

void Power_Close(AudioContext *pCxt)
{
    if (pCxt->state & STATE_AUDIOOUT) {
        AO_FlushBuffer(pCxt); // Should flush all the buffers
        AO_CloseHardware(pCxt);
    }
    else if (pCxt->state & STATE_AUDIOIN) {
        AI_CloseHardware(pCxt);
    }
    DBGOUT_FUNC(DBG_DUMP1, DumpAC97Regs());

    // The codec may not be closed completely
    // for CALL_HANDFREE and CALL_BLUETOOTH
    Codec_Close(pCxt);
    pCxt->state |= POWER_CLOSE;

    DBGOUT_FUNC(DBG_DUMP1, DumpAC97Regs());
}

#if (_TEST_TYPE == 41)      /* Test for Runtime of ISR */
void SetOutIsrTime(/* [out] */ MemoryBuf * pOutData)
{
    memcpy(pOutData->GetPayload(),
                ulDmaChOutIsrTime,
                sizeof(UInt32) * SAMPLETIMES);
    pOutData->SetUsed(SAMPLETIMES * sizeof(UInt32));
}
void SetRcvIsrTime(/* [out] */ MemoryBuf * pOutData)
{
    memcpy(pOutData->GetPayload(),
                ulDmaChRcvIsrTime,
                sizeof(UInt32) * SAMPLETIMES);
    pOutData->SetUsed(SAMPLETIMES * sizeof(UInt32));
}
#endif //_TEST_TYPE == 41
